home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / ARASAN_S.ZIP / CHESS.CPP < prev    next >
C/C++ Source or Header  |  1994-08-09  |  29KB  |  1,074 lines

  1. // Copyright 1994 by Jon Dart.  All Rights Reserved.
  2.  
  3. // main program.
  4.  
  5. #include "chess.h"
  6. #include "bhash.h"
  7. #include "bearing.h"
  8. #include "movegen.h"
  9. #include "msgs.h"
  10. #include "srclimit.h"
  11. #include "timectrl.h"
  12. #include "preferen.h"
  13. #include "display.h"
  14. #include "promotio.h"
  15. #include "showmove.h"
  16. #include "options.h"
  17. #include "notation.h"
  18. #include "movearr.h"
  19. #include "hint.h"
  20. #include "globals.h"
  21. #include <fstream.h>
  22. #include <ctype.h>
  23. #include <string.h>
  24. #include <time.h>
  25.  
  26. #define MAIN_TIMER 1
  27.  
  28. // global variables
  29.  
  30. Book *opening_book = NULL;
  31. WPMainWin *appWin = NULL;
  32. Move_Array *game_moves = NULL;
  33. Options *global_options = NULL;
  34. Clock *the_clock = NULL;
  35. static char tmpbuf[256];
  36.  
  37. Chess::Chess()
  38. {
  39.      createWin("Arasan");
  40.      WPRect area;
  41.      getWindowRect(area);
  42.      display = new Display(this,area);
  43.      the_clock = new Clock(getHwnd());
  44.      setTimer(MAIN_TIMER,1000);
  45.      global_options = new Options();
  46.      setup_clock_initially();
  47.      game_moves = new Move_Array();
  48.      opening_book = new Book();
  49.      searcher = new Search;
  50.      use_book = True;
  51.      users_move = True;
  52.      *last_move_image = '\0';
  53.      last_cmd = MYIDM_DUMMY;
  54.      no_prev_search = True;
  55.      log.write_header();
  56.      quitting = False;
  57. }
  58.  
  59. Chess::~Chess()
  60. {
  61.      quitting = True;
  62.      killTimer(MAIN_TIMER);
  63.      if (searching)
  64.      {
  65.          searcher->terminate_now();
  66.      }
  67.      delete the_clock;
  68.      delete display;
  69.      delete global_options;
  70.      delete game_moves;
  71.      delete opening_book;
  72.      delete searcher;
  73. }
  74.  
  75. void Chess::reset()
  76. {
  77.        delete game_moves;
  78.        game_moves = new Move_Array();
  79.        log.clear();
  80.        log.write_header();
  81.        last_move.MakeNull();
  82.        predicted_move.MakeNull();
  83.        *last_move_image = '\0';
  84.        use_book = True;
  85.        users_move = True;
  86.        last_cmd = MYIDM_DUMMY;
  87.        setup_clock_initially();
  88.        the_clock->reset();
  89.        no_prev_search = True;
  90.            computer_plays(current_board.OppositeSide());
  91.        {
  92.          WPWinDC dc = this;
  93.          display->clear_status_line(dc);
  94.          display->clear_move_area(dc);
  95.          display->clear_search_counts(dc);
  96.        }
  97.        stats.clear();
  98. }
  99.  
  100. void Chess::paint( WPPaintStruct &ps)
  101. {
  102.      WPRect clientArea = this;
  103.      display->set_size(clientArea);
  104.      WPRect drawArea;
  105.      ps.getPaintRect(drawArea);
  106.      display->draw_board(ps,current_board,&drawArea);
  107.      show_side();
  108.      show_last_move();
  109.      the_clock->show_time(White);
  110.      the_clock->show_time(Black);
  111.      if (!no_prev_search)
  112.         Display::show_search_counts(getHwnd(),stats.max_depth,
  113.       stats.num_pos);
  114.      WPWinDC dc(this);
  115.      display->show_status(dc,stats);
  116.      validate();
  117. }
  118.  
  119. static int legal_move(const Board &board, const Square &start,
  120.   const Square &dest)
  121. {
  122.     // do a little basic sanity checking:
  123.       if (!start.OnBoard() || !dest.OnBoard() || board[start].IsEmpty())
  124.           return False;
  125.  
  126.       ExtendedMove emove(board,start,dest);
  127.       Move moves[Move_Generator::MaxMoves];
  128.       Move_Generator mg( board, 0, Move::NullMove() );
  129.       int found = 0;
  130.           int n = mg.Generate_Moves(moves, False);
  131.  
  132.       for (int i = 0; i < n; i++)
  133.           {
  134.           if (moves[i].StartSquare() == emove.StartSquare() &&
  135.                 moves[i].DestSquare() == emove.DestSquare())
  136.           {
  137.              found++;
  138.              break;
  139.           }
  140.       }
  141.       if (!found)
  142.           return False;
  143.           else          
  144.       {
  145.           // check for king en prise
  146.           Board board_copy(board);
  147.           const ColorType side = board.Side();
  148.           board_copy.MakeMove(emove);
  149.           return board_copy.num_attacks(
  150.               board_copy.KingPos(side),OppositeColor(side)) == 0;
  151.           
  152.       }
  153. }
  154.  
  155. // handle updating to the next time control, if necessary
  156. void Chess::update_time()
  157. {
  158.        if (the_clock->time_is_up())
  159.            return;
  160.        // We already made the move and changed the side to move,
  161.        // so use "oppside" for the data we need:
  162.        const ColorType oppside = current_board.OppositeSide();
  163.        Time_Info &my_ti = ti[oppside];
  164.        if (my_ti.get_search_type() == Tournament)
  165.        {
  166.            // see if we made time control
  167.        Search_Limit limit = my_ti.get_search_limit();
  168.        if (((game_moves->num_moves()-1)/2) - my_ti.get_last_time_control()
  169.         + 1 == limit.limit.moves)
  170.        {
  171.            // we made it, update to next time control
  172.            int period = my_ti.get_period();
  173.            time_t bonus = the_clock->get_limit(oppside) 
  174.                      - the_clock->elapsed_time(oppside);
  175.              my_ti.set_bonus(bonus);
  176.            if (++period < Max_Time_Controls)
  177.            {
  178.               Time_Control next_tc = 
  179.                      global_options->get_time_control((Control)period);
  180.           if (next_tc.get_search_type() != None)
  181.           {
  182.              my_ti.set_time_control(next_tc);
  183.           }
  184.            }
  185.            my_ti.set_last_time_control(1+((game_moves->num_moves()-1)/2));
  186.            my_ti.set_period(period);
  187.            setup_clock(oppside);
  188.        }
  189.        }
  190.        the_clock->start(current_board.Side());
  191. }
  192.  
  193. void Chess::update_board( const ExtendedMove &emove,
  194.               const Search::Statistics *stats,
  195.               const Boolean update_log)
  196. {
  197.      WPWinDC dc(this);
  198.      if (stats)
  199.          display->show_status(dc,*stats);
  200.  
  201.      if (!emove.IsNull())
  202.      {
  203.         Notation::Image(current_board,emove,last_move_image);
  204.     if (update_log)
  205.            log.add_move(current_board,last_move,last_move_image,stats,True);
  206.         current_board.MakeMove(emove);
  207.         show_last_move( last_move_image ); // must call after add_move
  208.         game_moves->add_move(current_board,emove);
  209.  
  210.         display->draw_square(dc,emove.StartSquare());
  211.         display->draw_square(dc,emove.DestSquare());
  212.         display->draw_piece(dc,emove.DestSquare(),
  213.                   current_board[emove.DestSquare()]);
  214.         Square target, oldrooksq, newrooksq;
  215.         switch( emove.Special())
  216.         {
  217.        case ExtendedMove::Normal:
  218.           break;
  219.        case ExtendedMove::EnPassant:
  220.               target = emove.DestSquare();
  221.           if (emove.PieceMoved().Color() == White) 
  222.            target += RankIncr;
  223.           else
  224.              target -= RankIncr;
  225.           display->draw_square(dc,target);
  226.           break;
  227.            case ExtendedMove::KCastle:
  228.           oldrooksq = emove.StartSquare() + 3;
  229.            newrooksq = emove.StartSquare() + 1;
  230.             display->draw_square(dc,oldrooksq);
  231.           display->draw_piece(dc,newrooksq,current_board[newrooksq]);
  232.           break;
  233.        case ExtendedMove::QCastle:
  234.           oldrooksq = emove.StartSquare() - 4;
  235.           newrooksq = emove.StartSquare() - 1;
  236.             display->draw_square(dc,oldrooksq);
  237.           // must re-draw new rook square, so we get the right brush:
  238.           display->draw_square(dc,newrooksq);
  239.           display->draw_piece(dc,newrooksq,current_board[newrooksq]);
  240.           break;
  241.        case ExtendedMove::Promotion:
  242.           display->draw_piece(dc,emove.DestSquare(),
  243.             Piece(emove.PromoteTo(),emove.PieceMoved().Color()));
  244.           break;
  245.         }
  246.         display->show_side(dc,current_board.Side());
  247.         users_move = !users_move;
  248.      }
  249.  
  250.      if (stats)
  251.      {
  252.          switch (stats->state)
  253.          {
  254.              case Search::Checkmate:
  255.            MsgBox("Checkmate!");
  256.                the_clock->stop();
  257.            break;
  258.              case Search::Stalemate:
  259.            MsgBox("Stalemate!");
  260.                the_clock->stop();
  261.            break;
  262.          case Search::Draw:
  263.            MsgBox("Draw!");
  264.                the_clock->stop();
  265.            break;
  266.              case Search::Resigns:
  267.            {
  268.              char msg[40];
  269.          wsprintf(msg,"%s resigns!",
  270.             Image(current_board.OppositeSide()));
  271.              MsgBox(msg);
  272.            }
  273.                the_clock->stop();
  274.            break;
  275.              default:
  276.            update_time();
  277.          }
  278.      }
  279.      else
  280.      {
  281.          update_time();
  282.      }
  283.  
  284. }
  285.  
  286. void Chess::compute_move(Board &board, const Time_Info &ti,
  287.         const Boolean background,
  288.         Search::Statistics &stats,
  289.         ExtendedMove &emove)
  290. {
  291.       searching = True;
  292.  
  293.       if (global_options->use_book() && use_book)
  294.       {
  295.               Move move = opening_book->book_move(current_board);
  296.           if (!move.IsNull())
  297.           {
  298.               searching = False;
  299.                  emove = ExtendedMove(current_board,move);
  300.               if (current_board.CheckStatus() == Board::InCheck)
  301.                   stats.state = Search::Check;
  302.               else
  303.                    stats.state = Search::Normal;
  304.           stats.value = 0;
  305.           stats.elapsed_time = 0;
  306.           stats.num_moves = stats.num_pos = 0L;
  307.           if (background)
  308.              predicted_move = ReversibleMove(current_board,emove);
  309.           else
  310.              last_move = ReversibleMove(current_board,emove);
  311.           // suppress display of search statistics:
  312.           no_prev_search = True;
  313.           return;
  314.              }
  315.       }
  316.  
  317.           // no book move
  318.  
  319.       // The search routine has its own timer, so we disable ours.
  320.       killTimer(MAIN_TIMER);
  321.  
  322.       // We may be able to use the results of a search we did
  323.       // on our opponent's time:
  324.       Boolean previous_search = !users_move &&
  325.         global_options->think_when_idle();
  326.       Move best =
  327.                searcher->find_best_move(getHwnd(),board,
  328.            ti,
  329.            background,
  330.            stats,
  331.            predicted_move,
  332.            previous_search);
  333.           emove = ExtendedMove(current_board,best);
  334.       setTimer(MAIN_TIMER,1000);
  335.           if (!users_move && stats.state != Search::Terminated &&
  336.          global_options->beep_after_move())
  337.              MessageBeep(1);
  338.       if (background)
  339.          predicted_move = ReversibleMove(current_board,emove);
  340.       else
  341.              last_move = ReversibleMove(current_board,emove);
  342.       searching = False;
  343. }
  344.  
  345. BOOL Chess::mouse( int msg, WPPoint p, WORD /*flags*/ )
  346. {
  347.      Square dest;
  348.      switch (msg)
  349.      {
  350.      case WM_LBUTTONDOWN:
  351.        if (!users_move)
  352.        {
  353.           // not the user's move
  354.       if (global_options->beep_on_error())
  355.              MessageBeep(0);
  356.           start_square = Square::Invalid();
  357.       return FALSE;
  358.        }
  359.        // find out where we are on the board:
  360.        start_square = display->mouse_loc(p);
  361.        if (current_board[start_square].IsEmpty())
  362.        {
  363.           start_square = Square::Invalid();
  364.           return FALSE; // no piece on this square
  365.        }
  366.        else
  367.        {
  368.           WPWinDC dc(this);
  369.           display->highlight_square(dc,start_square);
  370.        }
  371.        break;
  372.      case WM_LBUTTONUP:
  373.        // find out where we are on the board:
  374.        dest = display->mouse_loc(p);
  375.        if (!dest.OnBoard())
  376.            return FALSE;
  377.        if (!users_move || start_square.IsInvalid())
  378.        {
  379.       return TRUE; // ignore event
  380.        }
  381.        if ( !legal_move(current_board,start_square,dest))
  382.        {
  383.           if (global_options->beep_on_error())
  384.              MessageBeep(8);
  385.        }
  386.        else
  387.        {
  388.           // move is legal
  389.           WPWinDC dc(this);
  390.           display->unhighlight_square(dc,start_square);
  391.       last_move = ReversibleMove(current_board,start_square,dest);
  392.       if (searching)
  393.       {
  394.          // tell the search to terminate
  395.          last_cmd = MYIDM_MAKEUSERMOVE;
  396.          searcher->terminate_now();
  397.       }
  398.       // if we are searching, this message won't be processed
  399.       // until we are done.
  400.           PostMessage(getHwnd(),WM_COMMAND,MYIDM_MAKEUSERMOVE,0L);
  401.       return TRUE;
  402.        }
  403.        break;
  404.      default:
  405.        break;
  406.      }
  407.      return TRUE;
  408. }
  409.  
  410. BOOL Chess::timer( int id )
  411. {
  412.     if (id == MAIN_TIMER)
  413.     {
  414.        the_clock->update();
  415.     }
  416.     return TRUE;
  417. }
  418.  
  419. BOOL Chess::sized(WPRect &box, WORD)
  420. {
  421.    display->set_size(box);
  422.    return FALSE;
  423. }
  424.  
  425. BOOL Chess::menuInit(WPMenu &menu)
  426. {
  427.     WPMenu mainMenu = this;
  428.     if (menu == mainMenu)
  429.     {
  430.         if (the_clock->is_running())
  431.     {
  432.            mainMenu.enableItem(MYIDM_PAUSE,TRUE);
  433.            mainMenu.enableItem(MYIDM_RESUME,FALSE);
  434.     }
  435.     else if (the_clock->is_stopped())
  436.     {
  437.        mainMenu.enableItem(MYIDM_PAUSE,FALSE);
  438.        mainMenu.enableItem(MYIDM_RESUME,FALSE);
  439.     }
  440.     else
  441.     {
  442.        mainMenu.enableItem(MYIDM_PAUSE,FALSE);
  443.        mainMenu.enableItem(MYIDM_RESUME,TRUE);
  444.     }
  445.     if (users_move)
  446.            mainMenu.enableItem(MYIDM_HINT,TRUE);
  447.     else
  448.            mainMenu.enableItem(MYIDM_HINT,FALSE);
  449.         return TRUE;
  450.     }
  451.     return FALSE;
  452. }
  453.  
  454. BOOL Chess::command( int id, WORD msg )
  455. {
  456.      // overrides base version, handles menu selections
  457.  
  458.      last_cmd = id;
  459.      if (id >= WM_USER)
  460.      {
  461.          if (quitting) // shutting down, ignore user commands
  462.          return TRUE; 
  463.          if (searching)
  464.          {
  465.              switch (id)
  466.          {
  467.             // These commands don't modify the board, the side to move,
  468.             // or the search parameters.  So there is no need to stop
  469.             // any search in progress.
  470.                 case MYIDM_SHOWGAME:
  471.             case MYIDM_ROTATEBOARD:
  472.             case MYIDM_PAUSE:
  473.             case MYIDM_RESUME:
  474.             case MYIDM_SAVEBOARD:
  475.             case MYIDM_PREFERENCES:
  476.             case MYIDM_RESTART_SEARCH:
  477.             case MYIDM_HINT:
  478.             case MYIDM_MAKEUSERMOVE: 
  479.             case MYIDM_DUMMY:
  480.                break;
  481.             default:
  482.                 // For all other commands, we first terminate the search.  All
  483.             // commands will be ignored until the search terminates.  At
  484.             // that point, we will execute 'last_cmd'.
  485.                searcher->terminate_now();
  486.                    PostMessage(getHwnd(),WM_COMMAND,id,0L);
  487.                return TRUE;
  488.          }
  489.      }
  490.      }
  491.      Square start_square,dest;
  492.      switch (id)
  493.      {
  494.         case MYIDM_DUMMY:
  495.         break;
  496.         case MYIDM_LOADBOARD:
  497.         *tmpbuf = '\0';
  498.         {
  499.             WPDlgFileOpen dlg(this,tmpbuf,"Load Board");
  500.         char filter[] = "Board file (*.fen)\0*.fen\0";
  501.         dlg.setFilter(filter);
  502.             if (!dlg.createWin())
  503.             {
  504.                 return FALSE;
  505.             }
  506.             if (*tmpbuf)
  507.             {
  508.                 ifstream pos_file( tmpbuf, 
  509.                 ios::in | ios::noreplace | ios::binary );
  510.                 if (pos_file.good())
  511.                 {
  512.                     pos_file >> current_board ;
  513.                 if (!pos_file)
  514.                 {
  515.                     MessageBox(NULL,"Bad format in file!",
  516.                 "Load Board",MB_OK);
  517.                     current_board.Reset();
  518.                 }
  519.             else
  520.                 use_book = False;
  521.                 pos_file.close();
  522.                 }
  523.             char *msg = new char[300];
  524.             wsprintf(msg,"== %s loaded from disk",tmpbuf);
  525.             log.write(msg);
  526.             log.write_eol();
  527.             delete [] msg;
  528.             reset();
  529.             draw_board();
  530.             show_side();
  531.             }
  532.         }
  533.         return TRUE;
  534.         case MYIDM_SAVEBOARD:
  535.       {
  536.         WPDlgFileSaveAs save_dlg(this,tmpbuf,"Save Board");
  537.         char filter[] = "Board file (*.fen)\0*.fen\0";
  538.         save_dlg.setFilter(filter);
  539.             if (!save_dlg.createWin())
  540.         {
  541.           return FALSE;
  542.         }
  543.         if (*tmpbuf)
  544.         {
  545.          ofstream ofile( tmpbuf,ios::out | ios::noreplace | ios::binary );
  546.          if (ofile.good())
  547.          {
  548.             ofile << current_board;
  549.         if (!ofile)
  550.            MessageBox(NULL,"Error saving file!","Save Board",MB_OK);
  551.         ofile.close();
  552.          }
  553.        }
  554.      }
  555.      return TRUE;
  556.         case MYIDM_LOADGAME:
  557.       // Games are stored in PGN notation.
  558.         *tmpbuf = '\0';
  559.         {
  560.             WPDlgFileOpen dlg(this,tmpbuf,"Load Game");
  561.             char filter[] = "Game file (*.pgn)\0*.pgn\0";
  562.         dlg.setFilter(filter);
  563.             if (!dlg.createWin())
  564.             {
  565.                 return FALSE;
  566.             }
  567.             if (*tmpbuf)
  568.             {
  569.             current_board.Reset();
  570.             log.clear();
  571.                 ifstream game_file( tmpbuf, ios::in );
  572.             ColorType side = White;
  573.             int c;
  574.             // skip header, if any:
  575.             while (game_file.good() && (c = game_file.get()) != EOF)
  576.             {
  577.                if (c=='[')
  578.                {
  579.                   game_file.ignore(255,']');
  580.               continue;
  581.                }
  582.                else if (c == '1')
  583.                {
  584.                   game_file.putback(c);
  585.                   break;
  586.                }
  587.             }
  588.             while (game_file.good() && !game_file.eof())
  589.             {
  590.                // skip to next move
  591.                for (;;)
  592.                {
  593.                   c = game_file.get();
  594.               if (isalpha(c) || game_file.eof())
  595.                  break;
  596.                }
  597.                // collect the move text
  598.                char movebuf[20];
  599.                int i = 0;
  600.                while (i < 19 && !game_file.eof() && 
  601.                          !isspace(c) && (c != '\n'))
  602.                {
  603.                   movebuf[i] = c; ++i;
  604.               c = game_file.get();
  605.                }
  606.                if (i == 0)
  607.                      break;
  608.                movebuf[i] = '\0';
  609.                // parse the move
  610.                Move m = Notation::Value(current_board,side,movebuf);
  611.                if (m.IsNull() ||
  612.                       !legal_move(current_board,m.StartSquare(),
  613.                    m.DestSquare()))
  614.                {
  615.                    char msg[80];
  616.                    wsprintf(msg,"Illegal or invalid move: %s",
  617.                    movebuf);
  618.                MessageBox(NULL,msg,"",MB_OK);
  619.                log.clear();
  620.                break;
  621.                }
  622.                else
  623.                {
  624.                    //MessageBox(NULL,m.Image(),"",MB_OK);
  625.                ExtendedMove emove(current_board,m);
  626.                ReversibleMove rmove(current_board,emove);
  627.                log.add_move(current_board,rmove,movebuf,NULL,
  628.                  False);
  629.                current_board.MakeMove(rmove);
  630.                }
  631.                side = OppositeColor(side);
  632.             }
  633.             game_file.close();
  634.              current_board.Reset();
  635.             log.reset();
  636.             last_move.MakeNull();
  637.             predicted_move.MakeNull();
  638.             *last_move_image = '\0';
  639.             use_book = True;
  640.             users_move = True;
  641.             last_cmd = MYIDM_DUMMY;
  642.             the_clock->reset();
  643.             no_prev_search = True;
  644.             computer_plays(Black);
  645.             {
  646.                WPWinDC dc = this;
  647.                display->clear_status_line(dc);
  648.                display->clear_move_area(dc);
  649.                display->clear_search_counts(dc);
  650.            }
  651.            stats.clear();
  652.            draw_board();
  653.                the_clock->start(current_board.Side());
  654.         }
  655.         return TRUE;
  656.        }
  657.     case MYIDM_SAVEGAME:
  658.       {
  659.         WPDlgFileSaveAs save_dlg(this,tmpbuf,"Save Game");
  660.         char filter[] = "Game file (*.pgn)\0*.pgn\0";
  661.         save_dlg.setFilter(filter);
  662.             if (!save_dlg.createWin())
  663.         {
  664.           return FALSE;
  665.         }
  666.         if (*tmpbuf)
  667.         {
  668.          ofstream ofile( tmpbuf,ios::out | ios::trunc );
  669.          if (ofile.good())
  670.          {
  671.             // Write standard PGN header.
  672.         time_t tm = time(NULL);
  673.         struct tm *t = localtime(&tm);
  674.         char string[15];
  675.             ofile << "[Event \"?\"]" << endl;
  676.         ofile << "[Site \"?\"]" << endl;
  677.         wsprintf(string,"%d.%02d.%02d",t->tm_year,t->tm_mon+1,
  678.             t->tm_mday);
  679.         ofile << "[Date \"" << string << "\"]" << endl;
  680.         ofile << "[Round \"nil\"]" << endl;
  681.         if (computer_side == White)
  682.         {
  683.            ofile << "[White \"" << "Arasan " << 
  684.                    Version << "\"]" << endl;
  685.            ofile << "[Black \"?\"]" << endl;
  686.         }
  687.         else
  688.         {
  689.            ofile << "[White \"?\"]" << endl;
  690.            ofile << "[Black \"" << "Arasan " << 
  691.                    Version << "\"]" << endl;
  692.         }
  693.         ofile << "[Result \"*\"]" << endl << endl;
  694.  
  695.         // Write game moves.
  696.         const unsigned size = log.num_moves();
  697.         char buf[80];
  698.         *buf = '\0';
  699.         char num[10];
  700.         int last = 0;
  701.         int num_image_size;
  702.         for (int i = 0; i < size; i++)
  703.         {
  704.             Log_Entry &e = log[i];
  705.             num_image_size = 0;
  706.             *num = '\0';
  707.             if (i % 2 == 0)
  708.             {
  709.                 wsprintf(num,"%d. ",(i/2)+1);
  710.             num_image_size = strlen(num);
  711.             }
  712.             const int image_size = strlen(e.image());
  713.             if (last + image_size + num_image_size + 1 >= 70)
  714.             {
  715.                 buf[last] = '\0';
  716.             ofile << buf << endl;
  717.             last = 0;
  718.             *buf = '\0';
  719.             }
  720.               if (last != 0)
  721.                strcat(buf," ");
  722.                 strcat(buf,num);
  723.             strcat(buf,e.image());
  724.             last = strlen(buf);
  725.         }
  726.         if (last)
  727.         {
  728.                buf[last] = '\0';
  729.            ofile << buf << endl;
  730.         }
  731.         if (!ofile)
  732.            MessageBox(NULL,"Error saving file!","Save Game",MB_OK);
  733.         ofile.close();
  734.          }
  735.        }
  736.      }
  737.       
  738.        return TRUE;
  739.         case MYIDM_EXIT:
  740.         WPMainWin::close();
  741.           return TRUE;
  742.         case MYIDM_BACKGROUND_COMPUTE:
  743.     {
  744.           if (!global_options->think_when_idle())
  745.                 return TRUE;
  746.           // We do a search to maximum depth.  We don't really
  747.           // expect to finish this; we will be interrupted
  748.           // by a user command.
  749.           Search_Limit limit;
  750.           limit.max_ply = Constants::MaxPly;
  751.           ExtendedMove emove;
  752.           Search::Statistics tmp_stats;
  753.           Time_Info ti;
  754.           ti.set_time_control(Time_Control( Fixed_Ply, limit ));
  755.           ti.set_period(0);
  756.           compute_move(current_board, ti,
  757.                    True, tmp_stats, emove);
  758.           return TRUE;
  759.     }
  760.     case MYIDM_RESTART_SEARCH:
  761.         case MYIDM_COMPUTE:
  762.     {
  763.       ExtendedMove emove;
  764.       {
  765.          WPWinDC dc(this);
  766.          display->clear_search_counts(dc);
  767.          display->clear_status_line(dc);
  768.          //display->clear_move_area(dc);
  769.       }
  770.       no_prev_search = False;
  771.       compute_move(current_board,ti[current_board.Side()],
  772.         False, stats, emove );
  773.           if (stats.state != Search::Terminated ||
  774.           last_cmd == MYIDM_COMPUTE)
  775.       {
  776.            last_move = ReversibleMove(current_board,emove);
  777.                update_board(emove,&stats,True);
  778.            if (users_move && global_options->think_when_idle())
  779.                   PostMessage(getHwnd(),WM_COMMAND,MYIDM_BACKGROUND_COMPUTE,0L);
  780.       }
  781.       return TRUE;
  782.     }
  783.     case MYIDM_MAKEUSERMOVE:
  784.     {
  785.       start_square = last_move.StartSquare();
  786.       dest = last_move.DestSquare();
  787.       ExtendedMove emove;
  788.           if (current_board[start_square].Type() == Piece::Pawn &&
  789.             dest.Rank(current_board.Side()) == 8)
  790.           {
  791.              // promotion, prompt for piece to promote to
  792.          PromotionOption popt;
  793.          popt.piece = 0; // default is 0 (Queen)
  794.          PromotionDialog *pdlg = new PromotionDialog(this,&popt);
  795.          if (pdlg->returnCode() == IDCANCEL)
  796.          {
  797.             delete pdlg;
  798.             return TRUE;
  799.          }
  800.          delete pdlg;
  801.          Move m;
  802.          switch (popt.piece)
  803.          {
  804.             case 0: m = Move(start_square,dest,Piece::Queen); break;
  805.             case 1: m = Move(start_square,dest,Piece::Rook); break;
  806.             case 2: m = Move(start_square,dest,Piece::Bishop); break;
  807.             case 3: m = Move(start_square,dest,Piece::Knight); break;
  808.          }
  809.          emove = ExtendedMove(current_board,m);
  810.           }
  811.       else
  812.          emove = ExtendedMove(current_board,start_square,dest);
  813.       update_board(emove,NULL,True);
  814.  
  815.       // calculate a reply move:
  816.       PostMessage(getHwnd(),WM_COMMAND,MYIDM_COMPUTE,0L);
  817.       return TRUE;
  818.     }
  819.         case MYIDM_TAKEBACK:
  820.       if (log.current() == 0)
  821.       {
  822.            MessageBox(NULL,"No moves to take back.","",MB_OK);
  823.          return TRUE;
  824.       }
  825.       else
  826.       {
  827.          current_board.UndoMove(log[log.current()-1].move());
  828.          log.back_up();
  829.          game_moves->remove_move();
  830.          if (log.current())
  831.          {
  832.             last_move = log[log.current()-1].move();
  833.         strcpy(last_move_image,log[log.current()-1].image());
  834.          }
  835.          else
  836.          {
  837.             last_move.MakeNull();
  838.         *last_move_image = '\0';
  839.          }
  840.          stats.clear();
  841.          show_last_move();
  842.          // should really be smarter about updating the board:
  843.          draw_board();
  844.          {
  845.             WPWinDC dc(this);
  846.                 display->clear_search_counts(dc);
  847.         display->clear_status_line(dc);
  848.          }
  849.          no_prev_search = True;
  850.          the_clock->start(current_board.Side());
  851.          users_move = !users_move;
  852.          return TRUE;
  853.       }
  854.         case MYIDM_FORWARD:
  855.           if (log.go_forward())
  856.       {
  857.           last_move = log.last_move();
  858.           update_board(last_move,NULL,False);
  859.       }
  860.       return TRUE;
  861.         case MYIDM_HINT:
  862.     {
  863.       HintDialog *dlg = new HintDialog(this,current_board);
  864.           if (dlg->returnCode() == IDCANCEL)
  865.       {
  866.           delete dlg;
  867.             return TRUE;
  868.       }
  869.       else
  870.       {
  871.           // got a move, execute it
  872.         delete dlg;
  873.         ExtendedMove emove(current_board,HintDialog::hintMove);
  874.             last_move = ReversibleMove(current_board,emove);
  875.         if (searching)
  876.         {
  877.         // tell the search to terminate
  878.            last_cmd = MYIDM_MAKEUSERMOVE;
  879.            searcher->terminate_now();
  880.         }
  881.         PostMessage(getHwnd(),WM_COMMAND,MYIDM_MAKEUSERMOVE,0L);
  882.         return TRUE;
  883.           }
  884.     }
  885.         case MYIDM_CHANGESIDES:
  886.       current_board.Set_Side(current_board.OppositeSide());
  887.       show_side();
  888.           the_clock->start(current_board.Side());
  889.       return TRUE;
  890.     case MYIDM_PLAYWHITE:
  891.       computer_plays( current_board.Side());
  892.       users_move = !users_move;
  893.       if (users_move)
  894.       {
  895.           if (global_options->think_when_idle())
  896.                  PostMessage(getHwnd(),WM_COMMAND,
  897.              MYIDM_BACKGROUND_COMPUTE,0L);
  898.       }
  899.       else
  900.          PostMessage(getHwnd(),WM_COMMAND,MYIDM_COMPUTE,0L);
  901.       return TRUE;
  902.         case MYIDM_ROTATEBOARD:
  903.       display->set_turned(!display->is_turned());
  904.           draw_board();
  905.       return TRUE;
  906.     case MYIDM_SHOWGAME:
  907.     {
  908.        ShowMovesDialog *sgdlg = new ShowMovesDialog(this,log);
  909.        delete sgdlg;
  910.        return TRUE;
  911.     }
  912.         case MYIDM_NEWGAME:
  913.        current_board.Reset();
  914.            draw_board();
  915.        reset();
  916.        use_book = True;
  917.        the_clock->start(White);
  918.        return TRUE;
  919.     case MYIDM_PAUSE:
  920.        the_clock->pause();
  921.        return TRUE;
  922.     case MYIDM_RESUME:
  923.        the_clock->resume();
  924.        return TRUE;
  925.     case MYIDM_RESET:
  926.        the_clock->reset();
  927.        return TRUE;
  928.         case MYIDM_SRCLIMITS:
  929.     {
  930.       Search_Limit_Options primary_options(global_options->get_time_control(First),First);
  931.       Search_Limit_Options secondary_options(global_options->get_time_control(Second),Second);
  932.       // Put the dialog box up.
  933.       // Note : Windows++ requires it to be on the heap, not stack:
  934.       SearchLimitDialog *sldlg = new SearchLimitDialog(this,
  935.           &primary_options,&secondary_options);
  936.       if( sldlg->isModified())
  937.       {
  938.          // update global_options with the new data
  939.          Time_Control new_tc;
  940.          primary_options.parse(new_tc,First);
  941.          global_options->set_time_control(new_tc,First);
  942.          char msg[100];
  943.          wsprintf(msg,"=== Primary time control: %s",new_tc.Image());
  944.          log.write(msg); log.write_eol();
  945.          if (new_tc.get_search_type() == Tournament)
  946.          { 
  947.                 secondary_options.parse(new_tc,Second);
  948.             wsprintf(msg,"=== Secondary time control: %s",new_tc.Image());
  949.             log.write(msg); log.write_eol();
  950.          }
  951.          else
  952.          {
  953.             new_tc.set_search_type(None);
  954.          }
  955.              global_options->set_time_control(new_tc,Second);
  956.          setup_clock_initially();
  957.          if (!users_move && searching)
  958.          {
  959.              // restart the search with the new limits
  960.              last_cmd = MYIDM_RESTART_SEARCH;
  961.              searcher->terminate_now();
  962.          }
  963.          delete sldlg;
  964.              return TRUE;
  965.       }
  966.       else return FALSE;
  967.       }
  968.         case MYIDM_PREFERENCES:
  969.     {
  970.        Preferences prefs;
  971.        global_options->get_preferences(prefs);
  972.        PreferencesDialog *pdlg = new PreferencesDialog(this,&prefs);
  973.        if( pdlg->isModified())
  974.        {
  975.           global_options->update_preferences(prefs);
  976.        }
  977.        delete pdlg;
  978.        return TRUE;
  979.     }
  980.     case MYIDM_LOSSONTIME:
  981.     {
  982.        char buf[50];
  983.        wsprintf(buf,"%s loses on time!",Image(the_clock->get_side_to_move()));
  984.        MsgBox(buf);
  985.        return TRUE;
  986.     }
  987.     default:
  988.       return WPMainWin::command(id,msg);
  989.      }
  990.      // shouldn't get here, but keep the compiler happy:
  991.      return TRUE;
  992. }
  993.  
  994. void Chess::draw_board()
  995. {
  996.        WPWinDC dc(this);
  997.         WPRect clientArea = this;
  998.     display->draw_board(dc,current_board,&clientArea);
  999. }
  1000.  
  1001. void Chess::show_side()
  1002. {
  1003.     WPWinDC dc(this);
  1004.     display->show_side(dc,current_board.Side());
  1005. }
  1006.  
  1007. void Chess::show_last_move( char *move_image )
  1008. {
  1009.     WPWinDC dc(this);
  1010.     display->show_move(dc,move_image,log.current());
  1011. }
  1012.  
  1013. void Chess::show_last_move()
  1014. {
  1015.      WPWinDC dc(this);
  1016.      if (last_move.IsNull())
  1017.         display->clear_move_area(dc);
  1018.      else
  1019.      {
  1020.         display->show_move(dc,last_move_image,log.current());
  1021.      }
  1022. }
  1023.  
  1024. void Chess::computer_plays( const ColorType side )
  1025. {
  1026.       computer_side = side;
  1027.       WPMenu menu = this;
  1028.       menu.checkItem(MYIDM_PLAYWHITE, side == White ? TRUE : FALSE,
  1029.       MF_BYCOMMAND );
  1030. }
  1031.  
  1032. void Chess::write_log(char *msg)
  1033. {
  1034.         log.write(msg); log.write_eol();
  1035. }
  1036.  
  1037. // Call this when a time control is reached to reset clock options for one
  1038. // side
  1039. void Chess::setup_clock(const ColorType side)
  1040. {
  1041.      const Search_Limit limits = ti[side].get_search_limit();
  1042.      switch (ti[side].get_search_type())
  1043.      {
  1044.          case Fixed_Ply:
  1045.      case Time_Limit:
  1046.          the_clock->count_up(); break;
  1047.      case Game:
  1048.          case Tournament:
  1049.          the_clock->count_down(
  1050.             limits.limit.minutes*60L+ti[side].get_bonus(),side);
  1051.              break;
  1052.      }
  1053. }
  1054.  
  1055. // Call this on startup and after clock options change
  1056. void Chess::setup_clock_initially()
  1057. {
  1058.      ti[White].set_time_control(global_options->get_time_control(First));
  1059.      ti[Black].set_time_control(global_options->get_time_control(First));
  1060.      ti[White].set_period(0);
  1061.      ti[Black].set_period(0);
  1062.      ti[White].set_bonus(0);
  1063.      ti[Black].set_bonus(0);
  1064.      setup_clock(White);
  1065.      setup_clock(Black);
  1066. }
  1067.  
  1068. void WPApp::main()
  1069. {
  1070.     mainWin = appWin = new Chess;
  1071.     run();
  1072. }
  1073.  
  1074.